﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

using Microsoft.Extensions.Caching.Memory;

using Newtonsoft.Json.Linq;

namespace Utils.Cache
{
    public class MemoryCacheStrategy : ICacheStrategy
    {

        private readonly IMemoryCache memory;

        public MemoryCacheStrategy(IMemoryCache memory)
        {
            this.memory = memory;
        }

        public ICacheStrategy Change(int db)
        {
            return new MemoryCacheStrategy(memory);
        }

        public void Add<T>(string key, T value, int seconds)
        {
            if (seconds == 0) return;
            memory.Set(key, value, DateTimeOffset.Now.AddSeconds(seconds));
        }


        public bool HasKey(string key)
        {
            foreach (var cacheKey in GetKeys(memory))
            {
                if (cacheKey.ToString() == key)
                {
                    return true;
                }
            }
            return false;
        }


        public object Get(string key)
        {
            return memory.Get(key);
        }

        public T Get<T>(string key)
        {
            if (memory.TryGetValue(key, out T value))
            {
                if (typeof(T).IsSerializable)
                    return value.JsonCopy<T>();
                return value;
            }
            return default(T);
        }


        public void RemoveAll()
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var entries = memory.GetType().GetField("_entries", flags).GetValue(memory);
            var cacheItems = entries as IDictionary;
            if (cacheItems == null) return;
            foreach (DictionaryEntry cacheItem in cacheItems)
            {
                memory.Remove(cacheItem.Key);
            }
        }

        public void Remove(string key)
        {
            memory.Remove(key);
        }

        public void RemoveStartsWith(string key)
        {
            foreach (object cacheKey in GetKeys(memory))
            {
                if (cacheKey.ToString().StartsWith(key))
                    memory.Remove(cacheKey);
            }
        }
        public static IEnumerable GetKeys(IMemoryCache memoryCache)
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            FieldInfo fieldInfo = memoryCache.GetType().GetField("_entries", flags);
            if (fieldInfo != null)
            {
                var entries = fieldInfo.GetValue(memoryCache) as IDictionary;
                if (entries != null)
                {

                    foreach (var item in entries.Keys)
                    {
                        yield return item;
                    }
                }
            }
            yield break;
        }

        public void DeleteHash(string key, string field)
        {
            if (HasKey(key))
            {
                var dict = memory.Get<IDictionary>(key);
                dict.Remove(field);
            }
        }

        public Dictionary<string, T> GetHashAll<T>(string key)
        {
            return memory.Get<Dictionary<string, T>>(key);
        }
        public void AddHash<T>(string key, string field, T value)
        {
            if (!HasKey(key))
            {
                memory.Set(key, new Dictionary<string, T>() { { field, value } });
            }
            else
            {
                var dict = memory.Get<Dictionary<string, T>>(key);
                dict[field] = value;
            }
        }

        public T GetHash<T>(string key, string field)
        {
            if (HasKey(key))
            {
                return memory.Get<Dictionary<string, T>>(key).TryGetValue(field) ?? default;
            }
            return default;
        }

        public void SetHash<T>(string key, IDictionary<string, T> fields)
        {
            if (fields == null)
            {
                return;
            }
            memory.Set(key, fields);
        }

        public void CloseChange()
        {
            memory.Dispose();
        }
    }
}
